home *** CD-ROM | disk | FTP | other *** search
/ Mac Power 1997 December / MACPOWER-1997-12.ISO.7z / MACPOWER-1997-12.ISO / AMUG / PROGRAMMING / Raven 1.2.sit / Raven 1.2 / Source / Foundation / Common / ZMiscUtils.cpp < prev    next >
Text File  |  1997-08-16  |  17KB  |  635 lines

  1. /*
  2.  *  File:       ZMiscUtils.cpp
  3.  *  Summary:       Misc utilities
  4.  *  Written by: Jesse Jones
  5.  *
  6.  *  Copyright ゥ 1996-1997 Jesse Jones. 
  7.  *    For conditions of distribution and use, see copyright notice in ZTypes.h  
  8.  *
  9.  *  Change History (most recent first):    
  10.  *
  11.  *         <8>     8/16/97    JDJ        ReportError now takes an OSStatus.
  12.  *         <7>     8/06/97    JDJ        ReportError uses StandardAlert.
  13.  *         <6>     7/27/97    JDJ        Added ByteSwap(double).
  14.  *         <5>     4/13/97    JDJ        TCodeTimer and TAverageTimer descend from MExitAction.
  15.  *         <4>     4/05/97    JDJ        Added a pragma unused.
  16.  *         <3>     1/09/97    JDJ        ReportError uses the Notification Manager if the
  17.  *                                    app is in the background.
  18.  *         <2>    12/05/96    JDJ        Added TAverageTimer.
  19.  *         <1>     1/13/96    JDJ        Created
  20.  */
  21.  
  22. #include <ZMiscUtils.h>
  23.  
  24. #include <Aliases.h>
  25. #include <Appearance.h>
  26. #include <Dialogs.h>
  27. #include <Errors.h>
  28. #include <Limits.h>
  29. #include <Memory.h>
  30. #include <Processes.h>
  31. #include <Resources.h>
  32. #include <StdIo.h>
  33. #include <String>
  34. #include <Timer.h>
  35. #include <ToolUtils.h>
  36.  
  37. #include <ZConstants.h>
  38. #include <ZDebug.h>
  39. #include <ZExceptions.h>
  40. #include <ZGestalt.h>
  41. #include <ZNotify.h>
  42. #include <ZStringUtils.h>
  43. #include <ZTypes.h>
  44.  
  45.  
  46. // ========================================================================================
  47. //    Internal Functions
  48. // ========================================================================================
  49.  
  50. //---------------------------------------------------------------
  51. //
  52. // IsInForeground
  53. //
  54. //---------------------------------------------------------------
  55. static bool IsInForeground()            
  56. {
  57.     Boolean inFront = true;
  58.  
  59.     ProcessSerialNumber    myProcess;
  60.     myProcess.highLongOfPSN = 0;
  61.     myProcess.lowLongOfPSN  = kCurrentProcess;
  62.  
  63.     ProcessSerialNumber    frontProcess;
  64.     OSErr err = GetFrontProcess(&frontProcess);
  65.     if (err == noErr)
  66.         err = SameProcess(&myProcess, &frontProcess, &inFront);
  67.     
  68.     ASSERT(err == noErr);
  69.     
  70.     return inFront;
  71. }
  72.  
  73.  
  74. //---------------------------------------------------------------
  75. //
  76. // DoStop
  77. //
  78. //---------------------------------------------------------------
  79. static short DoStop(ConstStr255Param str)    // takes a Str255 to avoid ambiguities with DoStop in ZDialogUtils.h
  80. {
  81.     InitCursor();
  82.     ParamText(str, "¥p", "¥p", "¥p");
  83.  
  84.     short item = StopAlert(130, nil);
  85.     
  86.     return item;
  87. }
  88.  
  89. #pragma mark -
  90.  
  91. // ========================================================================================
  92. //    Errors
  93. // ========================================================================================
  94.  
  95. //---------------------------------------------------------------
  96. //
  97. // ReportError (string, string, bool)
  98. //
  99. //---------------------------------------------------------------
  100. void ReportError(const string& errorStr, const string& supplementalStr, bool notify)
  101. {
  102.     #pragma unused(notify)
  103.     
  104. #if !DRAG_AND_DROP_APP                        // TNotify and StateBroadcaster are overkill for a simple d&d app
  105.     if (notify || !IsInForeground()) {        // ・・・ハThis seems like a pretty crude way to handle this...
  106.         TNotify* note = new TNotify(errorStr + supplementalStr);
  107.         note->Post();
  108.     
  109.     } else
  110. #endif
  111.     {
  112.         if (UGestalt::hasAppearanceMgr) {
  113.             AlertStdAlertParamRec params;
  114.             params.movable       = false;
  115.             params.helpButton    = false; 
  116.             params.filterProc    = nil;
  117.             params.defaultText   = (StringPtr) -1L;        // use default (ie "OK")
  118.             params.cancelText    = nil;        
  119.             params.otherText     = nil; 
  120.             params.defaultButton = ok;
  121.             params.cancelButton  = 0;
  122.             params.position      = kWindowDefaultPosition;
  123.  
  124.             short item;
  125.             OSErr err = StandardAlert(kAlertStopAlert, StrToPStr(errorStr), StrToPStr(supplementalStr), ¶ms, &item);
  126.             ASSERT(err == noErr);                        // don't throw
  127.  
  128.         } else
  129.             DoStop(StrToPStr(errorStr + supplementalStr));
  130.     }
  131. }
  132.  
  133.  
  134. //---------------------------------------------------------------
  135. //
  136. // ReportError (string, OSStatus, bool)
  137. //
  138. //---------------------------------------------------------------
  139. void ReportError(const string& errorStr, OSStatus err, bool notify)
  140. {
  141.     ASSERT(err != noErr);
  142.     
  143.     Str255 numberStr;
  144.     NumToString(err, numberStr);
  145.     
  146.     string supplementalStr = LoadRavenString(" because of an error #") + PStrToStr(numberStr);
  147.     
  148.     string text = LookUpString(203, err);
  149.     if (text != "")
  150.         supplementalStr +=  LoadRavenString(" (") + text + LoadRavenString(").");
  151.     else
  152.         supplementalStr += LoadRavenString(". (period)");
  153.  
  154.     ReportError(errorStr, supplementalStr, notify);
  155. }
  156.  
  157.  
  158. //---------------------------------------------------------------
  159. //
  160. // ReportError (string, TBaseException, bool)
  161. //
  162. //---------------------------------------------------------------
  163. void ReportError(const string& errorStr, const TBaseException& e, bool notify)
  164. {
  165.     if (const TSystemException* sysErr = dynamic_cast<const TSystemException*>(&e))
  166.         ReportError(errorStr, sysErr->mError, notify);
  167.     else
  168.         ReportError(errorStr, LoadRavenString(" because ") + e.what() + LoadRavenString(". (period)"), notify);
  169. }
  170.  
  171. #pragma mark -
  172.  
  173. // ========================================================================================
  174. //    Events
  175. // ========================================================================================
  176.  
  177. //---------------------------------------------------------------
  178. //
  179. // IsKeyDown
  180. //
  181. //---------------------------------------------------------------
  182. bool IsKeyDown(short keyCode)    
  183. {
  184.     Byte keyMap[16];
  185.  
  186.     GetKeys((UInt32 *) keyMap);
  187.     bool down = ((keyMap[keyCode >> 3] >> (keyCode & 7)) & 1) != 0;
  188.     
  189.     return down;
  190. }
  191.  
  192.  
  193. //---------------------------------------------------------------
  194. //
  195. // ExtractChar
  196. //
  197. // Returns the ASCII character corresponding to the keyDown or
  198. // keyUp event. This function can be used to find the character
  199. // that was pressed with the option key or to find out if the
  200. // shift key was held down with the command key. Note that this 
  201. // code is adapted from MacApp's TApplication::KeyEventToComponents. 
  202. //
  203. //---------------------------------------------------------------
  204. char ExtractChar(const EventRecord& event)
  205. {
  206.     ASSERT(event.what == keyDown || event.what == keyUp || event.what == autoKey);
  207.     
  208.     const int kMaskModifier = 0xF600;            // strip command and options keys from Modifiers 
  209.     const long kMaskASCII1 = 0x000000FF;        // get key from KeyTranslate return 
  210.     const long kMaskASCII2 = 0x00FF0000;        // get key from KeyTranslate return 
  211.     const short kUpKeyMask = 0x0080;
  212.  
  213.     // Now see if the command key is down. If it is, get the correct ASCII translation by
  214.     // masking the command key out and re-translating because the command key will
  215.     // mask the shift modifier.
  216.  
  217.     char theChar = (char) (event.message & charCodeMask);
  218.     ushort theKey = (ushort) ((event.message & keyCodeMask) >> 8);
  219.  
  220.     if ((event.modifiers & cmdKey) != 0 || (event.modifiers & optionKey) != 0) {
  221.         // Set the upkey bit so KeyTranslate doesn't do special deadkey processing. 
  222.         // See IM-V pp. 195 
  223.         ushort keyCodeParameter = (ushort) ((event.modifiers & kMaskModifier) | theKey | kUpKeyMask);
  224.  
  225.         Ptr keyTransTable = (Ptr) (GetScriptManagerVariable(smKCHRCache));
  226.  
  227.         ulong state = 0;
  228.         ulong keyInfo = KeyTranslate(keyTransTable, keyCodeParameter, &state);
  229.  
  230.         theChar = (char) (keyInfo & kMaskASCII1);
  231.         if (theChar == 0)
  232.             theChar = (char) ((keyInfo >> kMaskASCII2) & 16);
  233.     }
  234.     
  235.     return theChar;
  236.  
  237.  
  238. //---------------------------------------------------------------
  239. //
  240. // IsCommandPeriod
  241. //
  242. // Non-US keyboards sometimes use option key to generate period
  243. // so we'll strip off all the modifier keys before checking.
  244. //
  245. //---------------------------------------------------------------
  246. bool IsCommandPeriod(const EventRecord& event)
  247. {
  248.     bool cmdPeriod = false;
  249.     
  250.     if (event.what == keyDown)
  251.         if ((event.modifiers & cmdKey) != 0 && ExtractChar(event) == kPeriodChar)
  252.             cmdPeriod = true;
  253.  
  254.     return cmdPeriod;
  255. }
  256.  
  257. #pragma mark -
  258.  
  259. // ===================================================================================
  260. //    Misc
  261. // ===================================================================================
  262.  
  263. //---------------------------------------------------------------
  264. //
  265. // ByteSwap (double)
  266. //
  267. //---------------------------------------------------------------
  268. void ByteSwap(double& inData)
  269. {
  270.     ASSERT(sizeof(double) == 2*sizeof(long));
  271.     
  272.     long* data = reinterpret_cast<long*>(&inData);
  273.     
  274.     long temp = data[0];
  275.     data[0] = data[1];
  276.     data[1] = temp;
  277.     
  278.     byteswaplong(data[0]);
  279.     byteswaplong(data[1]);
  280. }
  281.  
  282.  
  283. //---------------------------------------------------------------
  284. //
  285. // BuildCRCTable
  286. //
  287. //---------------------------------------------------------------
  288. static unsigned long sCRCTable[256];
  289.  
  290. static void BuildCRCTable()
  291. {
  292.     const ulong kPolynomial = 0xEDB88320L;
  293.     
  294.     for (ulong i = 0; i < 256; i++) {
  295.         ulong value = i;
  296.         
  297.         for (int j = 8; j > 0; j--) {
  298.             if (value & 1)
  299.                 value = (value >> 1) ^ kPolynomial;
  300.             else
  301.                 value >>= 1;
  302.         }
  303.         
  304.         sCRCTable[i] = value;
  305.     }
  306. }
  307.  
  308.  
  309. //---------------------------------------------------------------
  310. //
  311. // ComputeCRC
  312. //
  313. // The code uses the CCITT-32 formula which is used by programs
  314. // like PKZIP and ARJ. The algorithm is taken from "The Data 
  315. // Compression Book" by Mark Nelson pg 446-447. 
  316. //
  317. //---------------------------------------------------------------
  318. ulong ComputeCRC(const void* buffer, ulong bytes, ulong crc)
  319. {
  320.     ASSERT(buffer != nil);
  321.     ASSERT(bytes < 16*1024L*1024L);
  322.     
  323.     static bool inited = false;
  324.     if (!inited) {
  325.         BuildCRCTable();
  326.         inited = true;
  327.     }
  328.         
  329.     // Note that these tests are not part of the original algorithm.
  330.     // They've been added because the prefs code sometimes tries to
  331.     // do a CRC on just a bool which doesn't work with the original 
  332.     // algorithm.
  333.     if (crc == 0xFFFFFFFFL && bytes == 1)        
  334.         crc = *((Byte *) buffer);
  335.         
  336.     else if (crc == 0xFFFFFFFFL && bytes == 2)
  337.         crc = *((ushort *) buffer);
  338.         
  339.     else if (crc == 0xFFFFFFFFL && bytes == 4)
  340.         crc = *((ulong *) buffer);
  341.  
  342.     else {
  343.         Byte* p = (Byte *) buffer;
  344.         
  345.         while (bytes--) {
  346.             ulong temp1 = (crc >> 8) & 0x00FFFFFFL;
  347.             ulong temp2 = sCRCTable[(crc ^ *p++) & 0xFF];
  348.             crc = temp1 ^ temp2;
  349.         }
  350.     }
  351.     
  352.     return crc;
  353. }
  354.  
  355.  
  356. //---------------------------------------------------------------
  357. //
  358. // GetMicroSeconds
  359. //
  360. //---------------------------------------------------------------
  361. long long GetMicroSeconds()
  362. {
  363.     UnsignedWide microSeconds;
  364.     Microseconds(µSeconds);
  365.  
  366.     long long seconds = microSeconds.hi;
  367.     seconds <<= 32;
  368.     seconds += microSeconds.lo;
  369.     
  370.     return seconds;
  371. }
  372.  
  373.  
  374. //---------------------------------------------------------------
  375. //
  376. // GetMilliSeconds
  377. //
  378. //---------------------------------------------------------------
  379. MilliSecond GetMilliSeconds()
  380. {
  381.     UnsignedWide microSeconds;
  382.     Microseconds(µSeconds);
  383.  
  384.     MilliSecond seconds = (MilliSecond) (microSeconds.hi*4294967L + microSeconds.lo/1000);        // 4294967L = 2^32 / 1000
  385.     
  386.     return seconds;
  387. }
  388.  
  389.  
  390. //---------------------------------------------------------------
  391. //
  392. // MilliSecondDelay
  393. //
  394. //---------------------------------------------------------------
  395. void MilliSecondDelay(MilliSecond delay)
  396. {
  397.     MilliSecond stopTime = GetMilliSeconds() + delay;
  398.  
  399.     while (GetMilliSeconds() < stopTime)
  400.         ;
  401. }
  402.  
  403. #pragma mark -
  404.  
  405. // ========================================================================================
  406. //    class TCodeTimer
  407. // ========================================================================================
  408.  
  409. //---------------------------------------------------------------
  410. //
  411. // TMOverhead
  412. //
  413. // Returns the overhead used by the Time Manager to set up a timer 
  414. // (in microseconds).
  415. //
  416. //---------------------------------------------------------------
  417. #if !RELEASE
  418. static long TMOverhead()
  419. {
  420.     long startTime = -LONG_MAX;
  421.  
  422.     TMTask task;
  423.     QElemPtr taskPtr = (QElemPtr) &task;
  424.  
  425.     task.tmAddr = nil;
  426.     task.tmWakeUp = 0;
  427.     task.tmReserved = 0;
  428.     
  429.     InsTime(taskPtr);
  430.     PrimeTime(taskPtr, startTime);
  431.     RmvTime(taskPtr);
  432.     
  433.     return -(startTime - task.tmCount);
  434. }
  435. #endif
  436.                         
  437.  
  438. //---------------------------------------------------------------
  439. //
  440. // TCodeTimer::~TCodeTimer
  441. //
  442. //---------------------------------------------------------------
  443. #if !RELEASE
  444. TCodeTimer::~TCodeTimer()
  445. {
  446.     // Returns the amount of unused time in tmCount (it should all be unused
  447.     // since we used a very large delay). The time will be in microseconds
  448.     // if the unused time is small or milliseconds if the delay is large.
  449.     ::RmvTime((QElemPtr) &mTask);
  450.     
  451.     long totalTime, endTime = mTask.tmCount;
  452.     
  453.     if (endTime < 0) 
  454.         totalTime = mStartTime - endTime;
  455.     else
  456.         totalTime = mStartTime + (1000L * endTime);
  457.  
  458.     totalTime = labs(totalTime) - TMOverhead();
  459.     if (totalTime < 0)                            // too quick to measure
  460.         totalTime = 0;
  461.  
  462.     DEBUGSTR("%s %.3f msecs.", mDescription, totalTime/1000.0);
  463. }
  464. #endif
  465.                         
  466.  
  467. //---------------------------------------------------------------
  468. //
  469. // TCodeTimer::TCodeTimer
  470. //
  471. //---------------------------------------------------------------
  472. #if !RELEASE
  473. TCodeTimer::TCodeTimer(const char* description)
  474. {
  475.     mDescription = description ? description : "Object lived for";
  476.     
  477.     // The Time Manager interprets positive numbers as millisecond values
  478.     // and negative numbers as negated microsecond values. Here we install
  479.     // a Time Manager task using microseconds that will wake up in 35 minutes.
  480.     mStartTime = -LONG_MAX;
  481.  
  482.     mTask.tmAddr     = nil;
  483.     mTask.tmWakeUp   = 0;
  484.     mTask.tmReserved = 0;
  485.     
  486.     ::InsTime((QElemPtr) &mTask);
  487.     ::PrimeTime((QElemPtr) &mTask, mStartTime);
  488. }
  489. #endif
  490.  
  491.  
  492. //---------------------------------------------------------------
  493. //
  494. // TCodeTimer::OnAbnormalExit
  495. //
  496. // The toolbox does not remove Time Manager tasks when an app shuts
  497. // down so we need to do it to prevent crashes.
  498. //
  499. //---------------------------------------------------------------
  500. #if !RELEASE
  501. void TCodeTimer::OnAbnormalExit()
  502. {
  503.     ::RmvTime((QElemPtr) &mTask);
  504. }
  505. #endif
  506.  
  507. #pragma mark -
  508.  
  509. // ========================================================================================
  510. //    class TAverageTimer
  511. // ========================================================================================
  512.  
  513. //---------------------------------------------------------------
  514. //
  515. // TAverageTimer::~TAverageTimer
  516. //
  517. //---------------------------------------------------------------
  518. #if !RELEASE
  519. TAverageTimer::~TAverageTimer()
  520. {
  521.     ASSERT(!mTiming);
  522.     
  523.     double average = mElapsedTime/mNumTimings;
  524.     
  525.     DEBUGSTR("%s %.3f msecs (average), %.3f (max), %.3f (min).", mDescription, average/1000.0, mMaxTime/1000.0, mMinTime/1000.0);
  526. }
  527. #endif
  528.                         
  529.  
  530. //---------------------------------------------------------------
  531. //
  532. // TAverageTimer::TAverageTimer
  533. //
  534. //---------------------------------------------------------------
  535. #if !RELEASE
  536. TAverageTimer::TAverageTimer(const char* description)
  537. {
  538.     mElapsedTime = 0.0;
  539.     mMaxTime     = 0;
  540.     mMinTime     = LONG_MAX;
  541.     mNumTimings  = 0;
  542.     mTiming      = false;
  543.     
  544.     mDescription = description ? description : "Object lived for";
  545. }
  546. #endif
  547.  
  548.  
  549. //---------------------------------------------------------------
  550. //
  551. // TAverageTimer::StartTiming
  552. //
  553. //---------------------------------------------------------------
  554. #if !RELEASE
  555. void TAverageTimer::StartTiming()
  556. {
  557.     ASSERT(!mTiming);
  558.  
  559.     // The Time Manager interprets positive numbers as millisecond 
  560.     // values and negative numbers as negated microsecond values. 
  561.     // Here we install a Time Manager task using microseconds that 
  562.     // will wake up in 35 minutes.
  563.     mStartTime = -LONG_MAX;
  564.  
  565.     mTask.tmAddr     = nil;
  566.     mTask.tmWakeUp   = 0;
  567.     mTask.tmReserved = 0;
  568.     
  569.     ::InsTime((QElemPtr) &mTask);
  570.     ::PrimeTime((QElemPtr) &mTask, mStartTime);
  571.     
  572.     mTiming = true;
  573. }
  574. #endif
  575.             
  576.  
  577. //---------------------------------------------------------------
  578. //
  579. // TAverageTimer::StopTiming
  580. //
  581. //---------------------------------------------------------------
  582. #if !RELEASE
  583. void TAverageTimer::StopTiming()
  584. {
  585.     ASSERT(mTiming);
  586.  
  587.     // Returns the amount of unused time in tmCount (it should all 
  588.     // be unused since we used a very large delay). The time will 
  589.     // be in microseconds if the unused time is small or milliseconds 
  590.     // if the delay is large.
  591.     ::RmvTime((QElemPtr) &mTask);
  592.     
  593.     long elapsed, endTime = mTask.tmCount;
  594.     
  595.     if (endTime < 0) 
  596.         elapsed = mStartTime - endTime;
  597.     else
  598.         elapsed = mStartTime + 1000L*endTime;
  599.  
  600.     elapsed = labs(elapsed) - TMOverhead();
  601.     if (elapsed < 0)                            // too quick to measure
  602.         elapsed = 0;
  603.     
  604.     if (elapsed < mMinTime)
  605.         mMinTime = elapsed;
  606.     
  607.     if (elapsed > mMaxTime)
  608.         mMaxTime = elapsed;
  609.     
  610.     mElapsedTime += elapsed;
  611.     mNumTimings++;
  612.     
  613.     mTiming = false;
  614. }
  615. #endif
  616.  
  617.  
  618. //---------------------------------------------------------------
  619. //
  620. // TAverageTimer::OnAbnormalExit
  621. //
  622. // The toolbox does not remove Time Manager tasks when an app shuts
  623. // down so we need to do it to prevent crashes.
  624. //
  625. //---------------------------------------------------------------
  626. #if !RELEASE
  627. void TAverageTimer::OnAbnormalExit()
  628. {
  629.     ::RmvTime((QElemPtr) &mTask);
  630. }
  631. #endif
  632.  
  633.  
  634.